// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using hub.Core.Client;
using hub.Core.Extensions;
using hub.Core.Listener;
using hub.Core.Models;
using hub.Core.Models.Massage;
using hub.core;
using Microsoft.Extensions.Logging;
using ExcelConfig;
using hub.data;

namespace hub.Core.Handlers.Server
{

    [JsonSourceGenerationOptions(WriteIndented = false)]
    [JsonSerializable(typeof(LogInMassage))]
    internal partial class SourceGenerationContext : JsonSerializerContext
    {
    }

    public class LoginHandler : ILoginHandler
    {
        private readonly ILogger logger;
        public const bool NeedRecive = true;

        public LoginHandler(ILogger<LoginHandler> logger)
        {
            this.logger = logger;
        }

        protected async Task HandleLoginAsync(FastTunnelServer server, TunnelClient client, LogInMassage requet, CancellationToken cancellationToken)
        {
            var hasTunnel = false;

            var tips = new List<string>();

            await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系（公网=>内网）", cancellationToken);
            await Task.Delay(300, cancellationToken);

            if (requet.Forwards != null && requet.Forwards.Any())
            {
                if (HUB.config.YesOrNo("开启转发"))
                {
                    hasTunnel = true;

                    foreach (var item in requet.Forwards)
                    {
                        try
                        {
                            if (item.LocalPort == 3389)
                                tips.Add("您已将3389端口暴露，请确保您的PC密码足够安全。");

                            if (item.LocalPort == 22)
                                tips.Add("您已将22端口暴露，请确保您的PC密码足够安全。");

                            if (server.ForwardList.TryGetValue(item.RemotePort, out var old))
                            {
                                logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
                                old.Listener.Stop();
                                server.ForwardList.TryRemove(item.RemotePort, out var _);
                            }

                            // TODO: 客户端离线时销毁
                            var ls = new PortProxyListener("0.0.0.0", item.RemotePort, logger, client.webSocket);
                            ls.Start(new ForwardDispatcher(logger, server, item));

                            var forwardInfo = new ForwardInfo<ForwardHandlerArg> { Listener = ls, Socket = client.webSocket, SSHConfig = item };

                            // TODO: 客户端离线时销毁
                            server.ForwardList.TryAdd(item.RemotePort, forwardInfo);
                            logger.LogDebug($"SSH proxy success: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");

                            client.AddForward(forwardInfo);
                            await client.webSocket.SendCmdAsync(MessageType.Log, $"  {item.Protocol}    | {HUB.config.Text("域名")}:{item.RemotePort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
                        }
                        catch (Exception ex)
                        {
                            logger.LogError($"SSH proxy error: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
                            logger.LogError(ex.Message);
                            await client.webSocket.SendCmdAsync(MessageType.Log, ex.Message, CancellationToken.None);
                            continue;
                        }
                    }
                }
                else
                {
                    await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.ForwardDisabled, CancellationToken.None);
                }
            }

            foreach (var item in tips)
            {
                await client.webSocket.SendCmdAsync(MessageType.Log, item, CancellationToken.None);
            }

            if (!hasTunnel)
                await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None);
        }


        public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd, CancellationToken cancellationToken)
        {
            var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd, SourceGenerationContext.Default.LogInMassage);
            if (msg == null) return false;
            await HandleLoginAsync(fastTunnelServer, tunnelClient, msg, cancellationToken);
            return NeedRecive;
        }
    }
}
